<?php

namespace Upfor\UpforPHP\Cache;

use Memcache as MemcacheExtension;
use BadFunctionCallException;
use InvalidArgumentException;
use Upfor\UpforPHP\Cache\AbstractCache;
use Upfor\UpforPHP\Interfaces\CacheInterface;

/**
 * Memcache
 */
class Memcache extends AbstractCache implements CacheInterface {

    const PERSISTENT = false;

    protected $options = [
        'servers' => [
            [
                'host' => '127.0.0.1',
                'port' => 11211,
                'weight' => 100,
            ],
        ],
        'ttl' => 0,
        'prefix' => '',
    ];

    function __construct($options = array()) {
        if (!extension_loaded('memcache')) {
            throw new BadFunctionCallException('Cache "Memcache" is not supported');
        }

        if (!empty($options)) {
            $this->options = array_merge($this->options, $options);
        }

        !empty($this->options['prefix']) && $this->prefix = $this->options['prefix'];

        self::$cacheObj = new MemcacheExtension;

        $servers = (array) $this->options['servers'];
        foreach ($servers as $row) {
            if (is_string($row) && strpos($row, 'unix:///') === 0) {
                self::$cacheObj->addServer($row, 0);
            } elseif (is_array($row)) {
                $host = empty($row['host']) ? '127.0.0.1' : $row['host'];
                $port = empty($row['port']) ? 11211 : (int) $row['port'];
                $weight = empty($row['weight']) ? 0 : (int) $row['weight'];

                self::$cacheObj->addServer($host, $port, $weight);
            } else {
                throw new InvalidArgumentException('Param is invalid: servers');
            }
        }
    }

    /**
     * Store data in the cache
     *
     * @param  string  $key
     * @param  mixed   $value
     * @param  integer $ttl
     * @return boolean
     */
    public function set($key, $value, $ttl = null) {
        if (is_null($ttl) || $ttl < 0) {
            $ttl = (int) $this->options['ttl'];
        }
        $expire = $ttl > 0 ? $ttl + time() : 0;

        return self::$cacheObj->set($this->createKey($key), $value, $expire);
    }

    /**
     * Returns TRUE if the cache key exists and FALSE if not
     *
     * @param  string $key Cache key
     * @return boolean
     */
    public function has($key) {
        return (self::$cacheObj->get($this->createKey($key)) !== false);
    }

    /**
     * Fetch data from the cache
     *
     * @param  string $key
     * @return mixed|false
     */
    public function get($key) {
        return self::$cacheObj->get($this->createKey($key));
    }

    /**
     * Delete data from the cache
     *
     * @param  string $key
     * @return boolean
     */
    public function delete($key) {
        return self::$cacheObj->delete($this->createKey($key));
    }

    /**
     * Clears the cache
     *
     * @return boolean
     */
    public function clear() {
        return self::$cacheObj->flush();
    }

    /**
     * Increment
     *
     * @param  string $key
     * @param  mixed  $value
     * @return mixed
     */
    public function increment($key, $value = 1) {
        return self::$cacheObj->increment($this->createKey($key), (int) $value);
    }

    /**
     * Decrement
     *
     * @param  string $key
     * @param  mixed  $value
     * @return mixed
     */
    public function decrement($key, $value = 1) {
        return self::$cacheObj->decrement($this->createKey($key), (int) $value);
    }

    /**
     * Is support persistent?
     *
     * @return boolean
     */
    public function persistent() {
        return self::PERSISTENT;
    }

    public function __call($method, $arguments) {
        if (!method_exists(self::$cacheObj, $method)) {
            throw new BadFunctionCallException("Memcache::{$method}() is not supported");
        }

        return call_user_func_array([self::$cacheObj, $method], $arguments);
    }

}
